# flake8: noqa: E501
import json
import os
from typing import Optional, Type

import aiohttp
import requests

from lagent.actions.base_action import AsyncActionMixin, BaseAction, tool_api
from lagent.actions.parser import BaseParser, JsonParser


class BINGMap(BaseAction):
    """必应地图插件，用于查询地图相关信息"""

    def __init__(
        self,
        key: Optional[str] = None,  # 必应地图 API 密钥
        description: Optional[dict] = None,  # 工具描述
        parser: Type[BaseParser] = JsonParser,  # 结果解析器
    ) -> None:
        super().__init__(description, parser)
        # 从环境变量或参数获取 API 密钥
        key = os.environ.get('BING_MAP_KEY', key)
        if key is None:
            raise ValueError('请设置必应地图 API 密钥(环境变量 BING_MAP_KEY 或参数 key)')
        self.key = key
        self.base_url = 'http://dev.virtualearth.net/REST/V1/'  # API 基础 URL

    @tool_api(explode_return=True)
    def get_distance(self, start: str, end: str) -> dict:
        """
        获取两个位置之间的距离(公里)。

        Args:
            start: 起点位置
            end: 终点位置

        Returns:
            dict: 距离信息
                * distance: 距离(公里)
        """
        # 构建请求 URL
        url = f"{self.base_url}Routes/Driving?o=json&wp.0={start}&wp.1={end}&key={self.key}"
        # 发送 GET 请求
        r = requests.get(url)
        data = json.loads(r.text)
        # 提取路线信息
        route = data['resourceSets'][0]['resources'][0]
        # 获取行驶距离
        distance = route['travelDistance']
        return dict(distance=distance)

    @tool_api(explode_return=True)
    def get_route(self, start: str, end: str) -> dict:
        """
        获取两个位置之间的路线。

        Args:
            start: 起点位置
            end: 终点位置

        Returns:
            dict: 路线信息
                * route: 路线指示列表
        """
        # 构建请求 URL
        url = f"{self.base_url}Routes/Driving?o=json&wp.0={start}&wp.1={end}&key={self.key}"
        # 发送 GET 请求
        r = requests.get(url)
        data = json.loads(r.text)
        # 提取路线信息
        route = data['resourceSets'][0]['resources'][0]
        itinerary = route['routeLegs'][0]['itineraryItems']
        # 提取路线文本说明
        route_text = []
        for item in itinerary:
            if 'instruction' in item:
                route_text.append(item['instruction']['text'])
        return dict(route=route_text)

    @tool_api(explode_return=True)
    def get_coordinates(self, location: str) -> dict:
        """
        获取位置的坐标。

        Args:
            location: 位置名称

        Returns:
            dict: 坐标信息
                * latitude: 纬度
                * longitude: 经度
        """
        url = self.base_url + 'Locations'
        params = {'query': location, 'key': self.key}
        response = requests.get(url, params=params)
        json_data = response.json()
        coordinates = json_data['resourceSets'][0]['resources'][0]['point']['coordinates']
        return dict(latitude=coordinates[0], longitude=coordinates[1])

    @tool_api(explode_return=True)
    def search_nearby(self,
                      search_term: str,  # 搜索关键词
                      places: str = 'unknown',  # 位置名称
                      latitude: float = 0.0,  # 纬度
                      longitude: float = 0.0,  # 经度
                      radius: int = 5000  # 搜索半径(米)
                      ) -> dict:
        """
        在指定位置附近搜索地点。可以使用位置名称或经纬度。

        Args:
            search_term: 搜索关键词
            places: 位置名称，默认为 'unknown'
            latitude: 纬度，默认为 0.0
            longitude: 经度，默认为 0.0
            radius: 搜索半径(米)，默认为 5000

        Returns:
            dict: 地点信息
                * places: 地点列表，每个地点包含名称和地址，最多返回5个
        """
        url = self.base_url + 'LocalSearch'
        # 如果提供了位置名称，获取其坐标
        if places != 'unknown':
            pos = self.get_coordinates(**{'location': places})
            latitude, longitude = pos[1]['latitude'], pos[1]['longitude']
            
        # 构建请求参数
        params = {
            'query': search_term,
            'userLocation': f'{latitude},{longitude}',
            'radius': radius,
            'key': self.key
        }
        
        # 发送请求并解析结果
        response = requests.get(url, params=params)
        response_data = json.loads(response.content)
        results = response_data['resourceSets'][0]['resources']
        
        # 提取地点信息
        addresses = []
        for result in results:
            name = result['name']
            address = result['Address']['formattedAddress']
            addresses.append(dict(name=name, address=address))
            if len(addresses) == 5:  # 最多返回5个结果
                break
        return dict(place=addresses)


class AsyncBINGMap(AsyncActionMixin, BINGMap):
    """异步版本的必应地图插件"""
    
    # 异步版本的方法实现与同步版本类似，
    # 主要区别是使用 aiohttp 进行异步 HTTP 请求
    # 这里省略了重复的注释...
